{-# OPTIONS -XMultiParamTypeClasses -XFlexibleInstances -XIncoherentInstances -XFunctionalDependencies #-}

import FTable  (FT (..), MyIx(..) )

curry2 fun (FT ((i1,i2),(j1,j2)) (d1,d2) f) = FT (i1,j1) d1 g
   where
      g x = fun (FT (i2,j2) d2 (\i -> f(x,i)))

curry3 fun (FT ((i1,i2,i3),(j1,j2,j3)) (d1,d2,d3) f) = (FT (a,b) d g)
   where
      g (x,y) = fun (FT (i3,j3) d3 (\i -> f(x,y,i)))
      a  = (i1,i2)
      b  = (j1,j2)
      d  = (d1,d2)

curry4 fun (FT ((i1,i2,i3,i4),(j1,j2,j3,j4)) (d1,d2,d3,d4) f) = (FT (a,b) d g)
   where
      g (x1,x2,x3) = fun (FT (i4,j4) d4 (\x -> f(x1,x2,x3,x)))
      a  = (i1,i2,i3)
      b  = (j1,j2,j3)
      d  = (d1,d2,d3)

curry5 fun (FT ((i1,i2,i3,i4,i5),(j1,j2,j3,j4,j5)) (d1,d2,d3,d4,d5) f) = (FT (a,b) d g)
   where
      g (x1,x2,x3,x4) = fun (FT (i5,j5) d5 (\x -> f(x1,x2,x3,x4,x)))
      a  = (i1,i2,i3,i4)
      b  = (j1,j2,j3,j4)
      d  = (d1,d2,d3,d4)

class Quadrature a b | a -> b where
   quad :: a -> b

instance (MyIx i, Integral i, Fractional a)
     => Quadrature (FT i a) a where
   quad (FT (a,b) d f) = (fromIntegral d) * (c + e)
      where
         c = 0.5 * (f a + f b)
         e = sum $ [f i | i <- range' (a+d,b-d) d]

instance (MyIx i, Integral i, Fractional a)
     => Quadrature (FT (i,i) a) a where
   quad ft = quad $ quad `curry2` ft

instance (MyIx i, Integral i, Fractional a)
     => Quadrature (FT (i,i,i) a) a where
   quad ft = quad $ quad `curry3` ft

instance (MyIx i, Integral i, Fractional a)
     => Quadrature (FT (i,i,i,i) a) a where
   quad ft = quad $ quad `curry4` ft

class Ord b => Interpolated a b c | a -> c where
   interpol :: b -> a -> c

instance (Integral i, Fractional a, RealFrac b)
     => Interpolated (FT i a) b a where
   interpol _ (FTC v) = v
   interpol x (FT (a,b) d f)
      | x <= a' = (f a)
      | x >= b' = (f b)
      | otherwise =  v0 + k' * (v1 - v0)

      where
         v0 = f $ x0
         v1 = f $ x0 + d
         k  = (x - x0') / d'
         x0 = d * ( ceiling $ ( x - a' ) / d' - 1 )

         x0' = fromIntegral x0
         a'  = fromIntegral a
         b'  = fromIntegral b
         d'  = fromIntegral d
         k'  = fromRational $ toRational k

instance (Integral i, Fractional a, RealFrac b1, RealFrac b2)
     => Interpolated (FT (i,i) a) (b1,b2) a where
   interpol _ (FTC v) = v
   interpol (x,y) ft  = interpol x $ interpol y `curry2` ft

instance (Integral i, Fractional a, RealFrac b1, RealFrac b2, RealFrac b3)
     => Interpolated (FT (i,i,i) a) (b1,b2,b3) a where
   interpol _ (FTC v)  = v
   interpol (x,y,z) ft = interpol (x,y) $ interpol z `curry3` ft